﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Text.RegularExpressions;

namespace Mefisto.Core.Extensions
{
    public static class StringExtensions
    {
        #region Path Sanitization

        /// <summary>
        /// Méthode permettant de supprimer les caractères interdits d'un chemin de dossier ou de fichier
        /// </summary>
        /// <param name="value">Chemin de dossier ou de fichier</param>
        /// <param name="replacementChar">Caractère de remplacement</param>
        /// <returns>Chemin de dossier ou de fichier corrigé</returns>
        public static string SanitizePath(this string value, char replacementChar)
        {
            Path.GetInvalidPathChars().Concat(Path.GetInvalidFileNameChars()).Distinct().ToList().ForEach(invalidCaracter => value = value.Replace(invalidCaracter, replacementChar));
            return value;
        }

        /// <summary>
        /// Méthode permettant de supprimer les caractères interdits d'un chemin de dossier ou de fichier
        /// </summary>
        /// <param name="value">Chemin de dossier ou de fichier</param>
        /// <param name="replacementString">Chaîne de caractères de remplacement</param>
        /// <returns>Chemin de dossier ou de fichier corrigé</returns>
        public static string SanitizePath(this string value, string replacementString)
        {
            Path.GetInvalidPathChars().Concat(Path.GetInvalidFileNameChars()).Distinct().ToList().ForEach(invalidCaracter => value = value.Replace(invalidCaracter.ToString(), replacementString));
            return value;
        }

        #endregion

        #region Serialization

        ///<summary>
        /// Méthode statique permettant de sérialiser l'objet sous forme de string
        ///</summary>
        ///<returns>Objet sérialisé sous forme de string</returns>
        public static string Serialize<T>(T value)
        {
            using (var memoryStream = new MemoryStream())
            {
                var serializer = new DataContractSerializer(typeof(T));
                serializer.WriteObject(memoryStream, value);
                memoryStream.Seek(0, SeekOrigin.Begin);
                var reader = new StreamReader(memoryStream, Encoding.UTF8);
                var content = reader.ReadToEnd();
                return content;
            }
        }

        ///<summary>
        /// Méthode statique permettant de déserialiser une string afin de reconstruire l'objet afférent
        ///</summary>
        ///<param name="serialized">Objet sérialisé sous forme de string</param>
        ///<returns>Objet</returns>
        public static T Deserialize<T>(string serialized)
        {
            using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(serialized)))
            {
                var serializer = new DataContractSerializer(typeof(T));
                var theObject = (T)serializer.ReadObject(stream);
                return theObject;
            }
        }

        #endregion

        #region Parameters Injection

        /// <summary>
        /// Méthode d'extension permettant d'injecter des paramètres dans une chaîne de caractères.
        /// </summary>
        /// <param name="text">Chaîne de caractères</param>
        /// <param name="parameterRegexText">Regex permettant de trouver les paramètres dans la chaîne. NULL pour utiliser la REGEX par défaut : [\s=]@(\w+)[\s,;()']</param>
        /// <param name="parameterFormat">Formattage du paramètre à remplacer par sa valeur. NULL pour utiliser le format par défaut : @{0}</param>
        /// <param name="parameters">Paramètres à injecter</param>
        /// <returns>Chaîne de caractères</returns>
        public static string InjectParameters(this string text, string parameterRegexText, string parameterFormat, params KeyValuePair<string, object>[] parameters)
        {
            //S'il n'y a pas de paramètres, on renvoie la chaîne intacte
            if (!parameters.Any())
                return text;
            //Si la regex fournie est NULL ou vide, on utilise la regex par défaut
            if (string.IsNullOrWhiteSpace(parameterRegexText))
                parameterRegexText = @"[\s=]@(\w+)[\s,;()']";
            //Si le formattage fourni est NULL ou vide, on utilise le formattage par défaut
            if (string.IsNullOrWhiteSpace(parameterFormat))
                parameterFormat = @"@{0}";
            //On définit le pattern de recherche de paramètres
            var parameterRegex = new Regex(parameterRegexText);
            //On associe les paramètres avec leur nom
            var parameterValues = parameters.ToDictionary(p => p.Key, p => p.Value);
            //On crée un dictionnaire pour stocker les afférences nom/valeur
            var parameterDictionary = parameterRegex.Matches(text).Cast<Match>().Select(m => m.Groups[1].Value).Distinct().ToDictionary(m => m, m => m.Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries));
            //On boucle sur les paramètres retrouvés dans la commande
            foreach (var parameter in parameterDictionary)
            {
                //On initialise les variables de travail
                var path = "";
                object value = null;
                //On boucle sur les différentes parties du paramètre
                foreach (var parameterPart in parameter.Value)
                {
                    path += parameterPart;
                    //On calcule la valeur si on ne l'a pas encore fait
                    if (!parameterValues.ContainsKey(parameterPart))
                    {
                        //Si la valeur est nulle à cet endroit, alors le paramètre trouvé dans la commande n'a pas de correspondance avec les objets fournis en paramètre
                        if (value == null)
                            continue;
                        //On calcule la valeur et on la stocke
                        var propertyInfo = value.GetType().GetProperty(parameterPart);
                        parameterValues.Add(path, propertyInfo.GetValue(value, null));
                    }
                    //On utilise la valeur stockée précédemment
                    value = parameterValues[path];
                    //On rajoute un point pour reproduire le nom du paramètre complet, lorsque l'on rajoutera le nom de la propriété suivante
                    path += ".";
                }
                //Si la valeur est nulle, on passe au paramètre suivant
                if (value == null)
                    continue;
                //On remplace le paramètre par la valeur correspondante
                text.Replace(string.Format(parameterFormat, parameter.Key), value.ToString());
            }
            //On renvoie la chaîne de caractères avec les paramètres injectés
            return text;
        }

        #endregion

        #region Html

        //Detect set of tags
        private static readonly Regex TagRegex1 = new Regex(@"<\s*([^ >]+)[^>]*>.*?<\s*/\s*\1\s*>");
        //Detect single tags
        private static readonly Regex TagRegex2 = new Regex(@"<[^>]+>");

        /// <summary>
        /// Méthode permettant de détecter si du texte contient du Html
        /// </summary>
        /// <param name="text">Texte</param>
        /// <returns>Booléen</returns>
        public static bool ContainsHtml(this string text)
        {
            return TagRegex1.IsMatch(text) || TagRegex2.IsMatch(text);
        }

        #endregion
    }
}